<?php

namespace App\Services;

use App\Repositories\AttachmentRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;

class UploadService
{
    /**
     * 错误信息
     */
    protected $error = '';

    /**
     * 存储区
     */
    protected $disk = '';

    /**
     * 上传处理回调
     */
    protected $callbacks = [];

    /**
     * @var AttachmentRepository
     */
    protected $attachment_repository;

    /**
     * 文件保存目录
     */
    protected $save_dir = '';

    function __construct(AttachmentRepository $repository)
    {
        $this->disk(config('app.attachment_upload_disk'));
        $this->attachment_repository = $repository;
    }

    /**
     * 表单上传
     *
     * @param string $form_file_name
     * @param int $user_id
     * @param Request $request
     * @param array $allowed_exts
     * @return bool
     */
    public function formUpload($form_file_name, $user_id, Request $request, $allowed_exts = ['JPG', 'jpg', 'JPEG', 'jpeg', 'PNG', 'png', 'GIF', 'gif', 'WEBP', 'webp', 'pdf', 'PDF', 'doc', 'docx', 'xlsx', 'xls'])
    {
        //判断请求中是否包含name=file的上传文件
        if (!$request->hasFile($form_file_name)) {
            $this->error = '上传文件为空！';
            return false;
        }
        $file = $request->file($form_file_name);
        //判断文件上传过程中是否出错
        if (!$file->isValid()) {
            $this->error = '文件上传出错！';
            return false;
        }

        //扩展名
        $ext = $file->getClientOriginalExtension();
        if (!$ext) {
            $file_info = getimagesize($file);
            if ($file_info && $file_info['mime']) {
                $ext = substr($file_info['mime'], 6);
            }
        }

        if (!in_array($ext, $allowed_exts)) {
            $this->error = '不允许上传该扩展名的文件！';
            return false;
        }


        //临时绝对路径
        $tmp_real_path = $file->getRealPath();
        $upload_path = '';
        if (strval($request->input('upload_path'))) {
            $upload_path = strval($request->input('upload_path')) . '/';
        }
        $filename = $this->getSaveFileName($ext, $upload_path . date('His') . '_' . intval($user_id) . '_');

        $attributes = [
            'name' => $file->getClientOriginalName(),
            'mime' => $file->getClientMimeType(),
            'size' => $file->getSize(),
        ];

        $res = $this->callback($tmp_real_path, $attributes);

        $result = Storage::disk($this->disk)->put($filename, $res);

        if (!$result) {
            $this->error = '文件写入出错！';
            return false;
        }
        $url = Storage::disk($this->disk)->url($filename);
        $this->saveAttachmentRecord($url, $user_id, $attributes);
        return $url;
    }

    /**
     * 获取错误信息
     *
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * 设置存储区
     *
     * @param string $disk
     * @return $this
     */
    public function disk($disk = '')
    {
        if ($disk == '') {
            $disk = config('filesystems.default');
        }
        if (!config('filesystems.disks.' . $disk)) {
            $disk = config('filesystems.default');
        }
        $this->disk = $disk;
        return $this;
    }

    /**
     * 设置回调
     *
     * @param array $callbacks
     */
    public function setCallbacks($callbacks)
    {
        $this->callbacks = $callbacks;
    }

    /**
     * 设置文件保存路径
     *
     * @param string $dir
     */
    public function setSaveDir(string $dir)
    {
        $this->save_dir = trim($dir, '/');
    }

    /**
     * 获取保存文件名
     *
     * @param string $ext
     * @param string $prefix
     * @param string $suffix
     * @return string
     */
    private function getSaveFileName($ext, $prefix = '', $suffix = '')
    {
        return ($this->save_dir ? trim($this->save_dir, '/') . '/' : date('Y/md/')) . $prefix . uniqid() . $suffix . '.' . $ext;
    }

    /**
     * 保存附件记录
     *
     * @param $url
     * @param $user_id
     * @param $attributes
     * @return mixed
     */
    private function saveAttachmentRecord($url, $user_id, $attributes)
    {
        $name = $attributes['name'];
        $mime = $attributes['mime'];
        $size = intval($attributes['size']);

        $mime_parse = explode('/', $mime);
        $attachment = $this->attachment_repository->create([
            'disk' => $this->disk,
            'name' => $name,
            'url' => $url,
            'mime' => $mime,
            'type' => reset($mime_parse),
            'size' => intval($size),
            'user_id' => intval($user_id),
        ]);
        return $attachment->id ?: false;
    }

    /**
     * 执行回调处理
     *
     * @param string $file
     * @param array $attributes
     * @return bool|string
     */
    private function callback($file, &$attributes)
    {
        if (!is_array($this->callbacks) || empty($this->callbacks)) {
            return file_get_contents($file);
        }

        if (isset($this->callbacks['rename'])) {
            $attributes['name'] = $this->callbacks['rename'];
        }

        if (isset($this->callbacks['image'])) {
            $image = Image::make($file);
            if ($this->callbackImage($image, $this->callbacks['image'], $attributes)) {
                return $image->getDriver()->encoder->result;
            }
        }

        return file_get_contents($file);
    }

    /**
     * 执行回调处理：针对图片
     *
     * @param $image
     * @param $callback
     * @param $attributes
     * @return bool
     */
    private function callbackImage(&$image, $callback, &$attributes)
    {
        $allowed_operation = ['cut', 'resize'];
        $operate_result = false;
        foreach ($callback as $operate => $options) {
            if (!in_array($operate, $allowed_operation)) {
                continue;
            }
            switch ($operate) {
                case 'cut':
                    $width = 0;
                    $height = 0;
                    //指定宽高
                    if (is_array($options) && isset($options['width']) || isset($options['height'])) {
                        $width = $options['width'];
                        $height = $options['height'];
                    } //指定比例（宽高比），留短边，切长边
                    elseif (is_numeric($options)) {
                        $width = $image->getWidth();
                        $height = $image->getHeight();
                        if ($width / $height > $options) {
                            $width = intval($height / $options);
                        } else {
                            $height = intval($width / $options);
                        }
                    }
                    if ($width > 0 && $height > 0) {
                        $image->fit($width, $height);
                    }

                    $operate_result = true;
                    break;
                case 'resize':
                    $width = $options['width'];
                    $height = $options['height'];
                    $image->resize($width, $height);
                    $operate_result = true;
                    break;
            }
        }

        if ($operate_result) {
            $image->psrResponse();
            $attributes['size'] = strlen($image->getDriver()->encoder->result);
        }
        return $operate_result;
    }
}
